﻿#pragma once


#include <atomic> 
#include <array> 
#include <memory>
#include <mutex>
#include <utility>


/**
 * 简单对象池实现
 *
 * example：
#include <thread>
#include <iostream>
#include <string>
#include "Pool.h"

static std::mutex outmtx;

int main()
{

	Pool<std::string, 10> pool("123456");
	for (auto i = 0; i < 12; ++i) {
		std::thread([&pool, i]() {
			auto s1 = pool.acquire();
			if (s1)
			{
				*s1 = std::to_string(i);
				outmtx.lock();
				std::cout << s1 << '\t' << pool.remaining() << '\n';
				outmtx.unlock();
			}
			pool.restore(s1);
			}
		).detach();
	}


	getchar();
	return 0;
}
 */

template<class T, std::size_t max>
class Pool {
	using pointer = T*;
	using const_pointer = const T*;
	using object = std::pair<std::unique_ptr<T>, bool>;
public:
	template<class... Args>
	Pool(Args &&...args);

	/**
	 * 对象池大小
	 */
	constexpr auto size()->std::size_t;

	/**
	 * 对象池大小
	 */
	constexpr auto capacity()->std::size_t;

	/**
	 * 剩余容量
	 */
	auto remaining()->std::size_t;

	/**
	 * 对象池是否可用
	 */
	auto available()->bool;

	/**
	 * 获取池子中的一个对象
	 * 有借有还，再借不难
	 * acquire 必须搭配 restore使用。否则会造成某个资源标志始终为used,从而无法再次使用该资源
	 */
	auto acquire()->pointer;

	/**
	 * 归还从池子中取走的对象
	 * 如若不归还，则取走的对象使用会被标志位used，无法再次利用
	 * 有借有还，再借不难
	 */
	auto restore(pointer& ptr) ->void;

private:
	std::mutex m_mtx;
	std::array<object, max> m_objs;
	std::atomic<std::size_t> m_unusedObjNum{ max };
};

template<class T, std::size_t max>
template<class ...Args>
inline Pool<T, max>::Pool(Args && ...args)
{
	for (std::size_t i = 0; i < max; ++i)
	{
		auto&& p = std::make_unique<T>(std::forward<Args>(args)...);
		auto&& pair = std::make_pair<std::unique_ptr<T>, bool>(std::move(p), false);
		m_objs[i] = std::move(pair);
	}
}

template<class T, std::size_t max>
inline constexpr auto Pool<T, max>::size() -> std::size_t
{
	return max;
}

template<class T, std::size_t max>
inline constexpr auto Pool<T, max>::capacity() -> std::size_t
{
	return max;
}

template<class T, std::size_t max>
inline auto Pool<T, max>::remaining() -> std::size_t
{
	return m_unusedObjNum.load();
}

template<class T, std::size_t max>
inline auto Pool<T, max>::available() -> bool
{
	return m_unusedObjNum.load() > 0;
}

template<class T, std::size_t max>
inline auto Pool<T, max>::acquire() -> pointer
{
	pointer ptr{ nullptr };
	if (available())
	{
		std::lock_guard<std::mutex> locker(m_mtx);
		for (auto&& [pptr, used] : m_objs)
		{
			if (!used)
			{
				--m_unusedObjNum;
				used = true;
				ptr = pptr.get();
				break;
			}
		}
	}
	return ptr;
}

template<class T, std::size_t max>
inline auto Pool<T, max>::restore(pointer& ptr) -> void
{
	if (!ptr) return;
	std::lock_guard<std::mutex> locker(m_mtx);
	for (auto&& [pptr, used] : m_objs)
	{
		if (pptr.get() == ptr)
		{
			++m_unusedObjNum;
			used = false;
			ptr = nullptr;
			break;
		}
	}
}


